#include "motion_detect.hpp"

/**
 * @brief 构造函数
 */
motion_detect::motion_detect()
{
    int history = 300;
    int varThreshold = 200;
    bool detectShadows = true;
    bsmog2 = cv::createBackgroundSubtractorMOG2(history, varThreshold, detectShadows);
}

/**
 * @brief 析构函数
 */
motion_detect::~motion_detect()
{
}

/**
 * @brief 检测函数
 * @param frame AVFrame 使用的是 YUV420P 格式
 */
bool motion_detect::detect(AVFrame* frame)
{
    int width = frame->width;
    int height = frame->height;

    // 逐像素将 Y 分量复制到 OpenCV Mat 转换为灰度图像，以便进行背景减法和运动检测
    cv::Mat gray(height, width, CV_8UC1);
    uint8_t* y_plane = frame->data[0];
    for (int i = 0; i < height; ++i) {
        for (int j = 0; j < width; ++j) {
            gray.at<uchar>(i, j) = y_plane[i * width + j];
        }
    }

    // 定义感兴趣区域（ROI），只处理视频帧的一部分
    // 这里使用了应用中的配置参数来确定裁剪区域
    cv::Rect crop_region(width * 0.1, height * 0.1, width * (0.9 - 0.1), height * (0.9 - 0.1));

    // 裁剪图像，只处理感兴趣区域
    cv::Mat crop_gray = gray(crop_region);

    // 使用高斯模糊去噪声
    cv::Mat Gaussian;
    cv::GaussianBlur(crop_gray, Gaussian, cv::Size(5, 5), 0);

    // 用于背景减法的前景掩码, 应用背景减法来提取前景
    cv::Mat fgmask;
    bsmog2->apply(Gaussian, fgmask);

    // 形态学操作去噪，先进行闭运算，再进行开运算
    cv::Mat temp1, temp2;
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
    cv::morphologyEx(fgmask, temp1, cv::MORPH_CLOSE, kernel);
    cv::morphologyEx(temp1, temp2, cv::MORPH_OPEN, kernel);

    // 查找前景区域的轮廓
    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(temp2, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    // 最小轮廓面积，根据用户设置的基本面积和视频缩放比例计算
    int min_contour_area = int(1000 * (0.9 * 0.9));

    // 遍历所有轮廓，过滤掉小面积的轮廓，判断是否有运动
    bool motion_in_frame = false;
    for (const auto& contour : contours) {
        double area = cv::contourArea(contour); // 计算轮廓的面积
        if (area > min_contour_area) // 如果轮廓面积大于设定的最小值，则认为有运动
        {
            motion_in_frame = true;
            break; // 找到第一个有效轮廓后立即退出循环
        }
    }

    // 如果检测到运动，则返回 true，表示检测到人的存在
    if (motion_in_frame)
        return true;
    else
        return false;
}
